package com.example.docmanagement.config;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.Random;

/**
 * Redis 缓存配置类
 * 配置 Spring Cache 使用 Redis 作为缓存存储
 * 
 * 功能：
 * - 配置缓存管理器
 * - 支持不同缓存使用不同的 TTL
 * - JSON 序列化支持
 * - 防止缓存雪崩（随机 TTL）
 */
@Configuration
@EnableCaching  // 启用 Spring Cache 注解支持
public class RedisConfig {

    /**
     * 配置缓存管理器
     * 支持不同缓存名称使用不同的 TTL
     * 
     * @param connectionFactory Redis 连接工厂
     * @return 缓存管理器
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 为 Redis 序列化配置带有 Java 8 时间支持的 ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 为了在反序列化时恢复具体类型，开启多态类型信息（与 GenericJackson2JsonRedisSerializer 默认行为保持一致）
        objectMapper.activateDefaultTyping(
                objectMapper.getPolymorphicTypeValidator(),
                DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY
        );

        GenericJackson2JsonRedisSerializer jsonSerializer =
                new GenericJackson2JsonRedisSerializer(objectMapper);

        // 默认缓存配置（1小时，带随机偏移防止缓存雪崩）
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1).plus(Duration.ofSeconds(
                        new Random().nextInt(300)  // 随机增加 0-5 分钟，防止缓存雪崩
                )))
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(jsonSerializer))
                .disableCachingNullValues();  // 不缓存 null 值

        // 文档详情缓存配置（30分钟）
        RedisCacheConfiguration documentDetailConfig = defaultConfig
                .entryTtl(Duration.ofMinutes(30).plus(Duration.ofSeconds(
                        new Random().nextInt(180)  // 随机增加 0-3 分钟
                )))
                .prefixCacheNameWith("doc:detail:");

        // 文档列表缓存配置（10分钟）
        RedisCacheConfiguration documentListConfig = defaultConfig
                .entryTtl(Duration.ofMinutes(10).plus(Duration.ofSeconds(
                        new Random().nextInt(120)  // 随机增加 0-2 分钟
                )))
                .prefixCacheNameWith("doc:list:");

        // 分类树缓存配置（1小时）
        RedisCacheConfiguration categoryTreeConfig = defaultConfig
                .entryTtl(Duration.ofHours(1).plus(Duration.ofSeconds(
                        new Random().nextInt(300)  // 随机增加 0-5 分钟
                )))
                .prefixCacheNameWith("category:tree:");

        // 分类详情缓存配置（1小时）
        RedisCacheConfiguration categoryDetailConfig = defaultConfig
                .entryTtl(Duration.ofHours(1).plus(Duration.ofSeconds(
                        new Random().nextInt(300)  // 随机增加 0-5 分钟
                )))
                .prefixCacheNameWith("category:detail:");

        // 构建缓存管理器
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(defaultConfig)
                .withCacheConfiguration("documents", documentDetailConfig)
                .withCacheConfiguration("documentList", documentListConfig)
                .withCacheConfiguration("categories", categoryTreeConfig)
                .withCacheConfiguration("categoryDetail", categoryDetailConfig)
                .transactionAware()  // 支持事务
                .build();
    }
}

